package yl.hs.bmipfull.utils;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.ss.usermodel.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.FileCopyUtils;
import yl.hs.bmipfull.annotations.Excel;
import yl.hs.bmipfull.viewmodels.system.ExcelMap;

import java.io.*;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;


public class ExcelUtil<T> {
    private final Logger log = LoggerFactory.getLogger(ExcelUtil.class);

    private String tmpName;
    private List<Field> fields;
    public ExcelUtil(Class<?> cls){
        fields = new ArrayList<>();
        fields.addAll(Arrays.asList(cls.getSuperclass().getDeclaredFields()));
        fields.addAll(Arrays.asList(cls.getDeclaredFields()));
        tmpName = cls.getTypeName();
    }

    public byte[] exportByTemplate(String tmpFilePath, int startRow, List<T> data, List<ExcelMap> maps) throws IOException, InvalidFormatException {
        if(Tl.isEmpty(tmpFilePath)){
            throw new FileNotFoundException("未配置导出模板文件");
        }
        File file = new File(tmpFilePath);
        if(file.exists()) {
            InputStream inp = new FileInputStream(tmpFilePath);
            Workbook wb = WorkbookFactory.create(inp);
            Sheet sheet = wb.getSheetAt(0);
            var row = sheet.getRow(startRow);
            loadData(sheet,startRow, data, maps);
           var fileTmp = File.createTempFile(tmpName,".xlsx");
           var out = new FileOutputStream(fileTmp);
            wb.write(out);
            out.close();
            var byteData = fileToBinArray(fileTmp);
            fileTmp.delete();
            return byteData;
        }
        else
            throw new FileNotFoundException("导出模板文件不存在");
    }

    /**
     * 文件转为二进制数组
     * @param file
     * @return
     */
    public static byte[] fileToBinArray(File file){
        try {
            InputStream fis = new FileInputStream(file);
            byte[] bytes = FileCopyUtils.copyToByteArray(fis);
            return bytes;
        }catch (Exception ex){
            throw new RuntimeException("transform file into bin Array 出错",ex);
        }
    }

    private void fillCell(Cell cell,Field field,T vo){
        try {
            if (field.isAnnotationPresent(Excel.class)) {
                var attr = field.getAnnotation(Excel.class);
                var value = Tl.getFieldValue(field,vo);
                String dateFormat = attr.dateFormat();
                String readConverterExp = attr.readConverterExp();
                String separator = attr.separator();
                if (StringUtils.isNotEmpty(dateFormat) && value instanceof Date)
                {
                    cell.setCellValue(parseDateToStr(dateFormat, (Date) value));
                }
                else if (StringUtils.isNotEmpty(readConverterExp) && value!=null)
                {
                    cell.setCellValue(convertByExp(value.toString(), readConverterExp, separator));
                }
                else if (value instanceof BigDecimal)
                {
                    cell.setCellValue(((BigDecimal) value).doubleValue());
                }
                else {
                    cell.setCellValue((String) value);
                }
            }
        }
        catch (Exception ee){
            log.error("导出数据异常字段"+field.getName(),ee);
        }
    }


    private String convertByExp(String source, String readConverterExp, String separator) {
        var array = readConverterExp.split(separator);
        var value = "";
        for(var a:array){
            if(a.startsWith(source)){
                value = a.split("=")[1];
                break;
            }
        }
        return value;
    }

    private String parseDateToStr(String pattern,Date d){
        SimpleDateFormat format = new SimpleDateFormat(pattern);
        return format.format(d);
    }

    private Cell addCell(Row row,ExcelMap map,T vo){
       Cell cell = row.createCell(map.getColumn());


       if(!Tl.isEmpty(map.getFieldCode())){
           var fs = fields.stream().filter(f->map.getFieldCode().contains(f.getName())).toList();
           if(fs.size()>1){
               StringBuilder sb = new StringBuilder();
               for (var f:fs){
                   try {
                       sb.append(Tl.getFieldValue(f, vo).toString());
                   }
                   catch (Exception ee){}
               }
               cell.setCellValue(sb.toString());
           }
           else if(fs.size()==1)
           {
               fillCell(cell,fs.get(0),vo);
           }
       }
       else{
           if(map.getDefValue() instanceof Double) {
               cell.setCellValue((Double)map.getDefValue());
           }
           else if(map.getDefValue() instanceof Boolean){
               cell.setCellValue((Boolean)map.getDefValue());
           }
           else{
               cell.setCellValue((String) map.getDefValue());
           }
       }
       return  cell;
    }

    private void loadData(Sheet sheet,int startRow, List<T> data, List<ExcelMap> maps){
        for(var d : data) {
            Row row = sheet.createRow(startRow);
            for (var m:maps){
                addCell(row,m,d);
            }
            startRow++;
        }
    }
}
